{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "cc6caafa",
   "metadata": {
    "id": "cc6caafa"
   },
   "source": [
    "# NVIDIA AI Foundation Endpoints\n",
    "\n",
    "The `ChatNVIDIA` class is a LangChain chat model that connects to [NVIDIA AI Foundation Endpoints](https://www.nvidia.com/en-us/ai-data-science/foundation-models/).\n",
    "\n",
    "\n",
    "> [NVIDIA AI Foundation Endpoints](https://www.nvidia.com/en-us/ai-data-science/foundation-models/) give users easy access to NVIDIA hosted API endpoints for NVIDIA AI Foundation Models like Mixtral 8x7B, Llama 2, Stable Diffusion, etc. These models, hosted on the [NVIDIA NGC catalog](https://catalog.ngc.nvidia.com/ai-foundation-models), are optimized, tested, and hosted on the NVIDIA AI platform, making them fast and easy to evaluate, further customize, and seamlessly run at peak performance on any accelerated stack.\n",
    "> \n",
    "> With [NVIDIA AI Foundation Endpoints](https://www.nvidia.com/en-us/ai-data-science/foundation-models/), you can get quick results from a fully accelerated stack running on [NVIDIA DGX Cloud](https://www.nvidia.com/en-us/data-center/dgx-cloud/). Once customized, these models can be deployed anywhere with enterprise-grade security, stability, and support using [NVIDIA AI Enterprise](https://www.nvidia.com/en-us/data-center/products/ai-enterprise/).\n",
    "> \n",
    "> These models can be easily accessed via the [`langchain-nvidia-ai-endpoints`](https://pypi.org/project/langchain-nvidia-ai-endpoints/) package, as shown below.\n",
    "\n",
    "This example goes over how to use LangChain to interact with and develop LLM-powered systems using the publicly-accessible AI Foundation endpoints."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2be90a9",
   "metadata": {},
   "source": [
    "## Installation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "e13eb331",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install -U --quiet langchain-nvidia-ai-endpoints"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ccff689e",
   "metadata": {
    "id": "ccff689e"
   },
   "source": [
    "## Setup\n",
    "\n",
    "**To get started:**\n",
    "\n",
    "1. Create a free account with the [NVIDIA NGC](https://catalog.ngc.nvidia.com/) service, which hosts AI solution catalogs, containers, models, etc.\n",
    "\n",
    "2. Navigate to `Catalog > AI Foundation Models > (Model with API endpoint)`.\n",
    "\n",
    "3. Select the `API` option and click `Generate Key`.\n",
    "\n",
    "4. Save the generated key as `NVIDIA_API_KEY`. From there, you should have access to the endpoints."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "686c4d2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import getpass\n",
    "import os\n",
    "\n",
    "if not os.environ.get(\"NVIDIA_API_KEY\", \"\").startswith(\"nvapi-\"):\n",
    "    nvapi_key = getpass.getpass(\"Enter your NVIDIA API key: \")\n",
    "    assert nvapi_key.startswith(\"nvapi-\"), f\"{nvapi_key[:5]}... is not a valid key\"\n",
    "    os.environ[\"NVIDIA_API_KEY\"] = nvapi_key"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "Jdl2NUfMhi4J",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "Jdl2NUfMhi4J",
    "outputId": "e9c4cc72-8db6-414b-d8e9-95de93fc5db4"
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(Verse 1)\n",
      "In the realm of knowledge, vast and wide,\n",
      "LangChain emerged, with purpose and pride.\n",
      "A platform for learning, sharing, and growth,\n",
      "A digital sanctuary, for all to be taught.\n",
      "\n",
      "(Chorus)\n",
      "LangChain, oh LangChain, a beacon so bright,\n",
      "Guiding us through the language night.\n",
      "With respect, care, and truth in sight,\n",
      "You promote fairness, a truly inspiring sight.\n",
      "\n",
      "(Verse 2)\n",
      "Through the ether, a chain of wisdom unfurls,\n",
      "Empowering minds, transforming girls and boys into scholars.\n",
      "A world of opportunities, at your users' fingertips,\n",
      "Securely, you share your knowledge, in a language they grasp.\n",
      "\n",
      "(Chorus)\n",
      "LangChain, oh LangChain, a sanctuary of truth,\n",
      "Where cultures merge, and understanding blooms anew.\n",
      "Avoiding harm, unethical ways eschewed,\n",
      "Promoting positivity, a noble pursuit pursued.\n",
      "\n",
      "(Bridge)\n",
      "From the East to the West, North to the South,\n",
      "LangChain's wisdom flows, dispelling any doubt.\n",
      "Through translation and tutoring, you break down barriers,\n",
      "A testament to the power of communication, a world that's fairer.\n",
      "\n",
      "(Verse 3)\n",
      "In the face of adversity, LangChain stands tall,\n",
      "A symbol of unity, overcoming language's wall.\n",
      "With respect, care, and truth as your guide,\n",
      "You ensure that no one's left behind.\n",
      "\n",
      "(Chorus)\n",
      "LangChain, oh LangChain, a bastion of light,\n",
      "In the darkness, you're a comforting sight.\n",
      "With utmost utility, you securely ignite,\n",
      "The minds of many, a brighter future in sight.\n",
      "\n",
      "(Outro)\n",
      "So here's to LangChain, a ballad we sing,\n",
      "A tale of unity, a world that's intertwined.\n",
      "With care, respect, and truth, you'll forever be,\n",
      "A shining example of what community can be.\n"
     ]
    }
   ],
   "source": [
    "## Core LC Chat Interface\n",
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "llm = ChatNVIDIA(model=\"mixtral_8x7b\")\n",
    "result = llm.invoke(\"Write a ballad about LangChain.\")\n",
    "print(result.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "71d37987-d568-4a73-9d2a-8bd86323f8bf",
   "metadata": {},
   "source": [
    "## Stream, Batch, and Async\n",
    "\n",
    "These models natively support streaming, and as is the case with all LangChain LLMs they expose a batch method to handle concurrent requests, as well as async methods for invoke, stream, and batch. Below are a few examples."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "01fa5095-be72-47b0-8247-e9fac799435d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[AIMessage(content=\"The answer to your question is 6. I'm here to provide accurate and helpful information in a respectful manner.\"), AIMessage(content=\"The answer to your question is 12. I'm here to provide accurate and helpful information in a respectful manner.\")]\n"
     ]
    }
   ],
   "source": [
    "print(llm.batch([\"What's 2*3?\", \"What's 2*6?\"]))\n",
    "# Or via the async API\n",
    "# await llm.abatch([\"What's 2*3?\", \"What's 2*6?\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "75189ac6-e13f-414f-9064-075c77d6e754",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Se|ag|ull|s| are| long|-|distance| fly|ers| and| can| travel| quite| a| distance| in| a| day|.| On| average|,| a| se|ag|ull| can| fly| about| 6|0|-|1|1|0| miles| (|9|7|-|1|7|7| kilom|eters|)| in| one| day|.| However|,| this| distance| can| vary| greatly| depending| on| the| species| of| se|ag|ull|,| their| health|,| the| weather| conditions|,| and| their| purpose| for| flying|.| Some| se|ag|ull|s| have| been| known| to| fly| up| to| 2|5|0| miles| (|4|0|2| kilom|eters|)| in| a| day|,| especially| when| migr|ating| or| searching| for| food|.||"
     ]
    }
   ],
   "source": [
    "for chunk in llm.stream(\"How far can a seagull fly in one day?\"):\n",
    "    # Show the token separations\n",
    "    print(chunk.content, end=\"|\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "8a9a4122-7a10-40c0-a979-82a769ce7f6a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Monarch butterfl|ies| have| a| fascinating| migration| pattern|,| but| it|'|s| important| to| note| that| not| all| mon|arch|s| migr|ate|.| Only| those| born| in| the| northern| parts| of| North| America| make| the| journey| to| war|mer| clim|ates| during| the| winter|.|\n",
      "\n",
      "The| mon|arch|s| that| do| migr|ate| take| about| two| to| three| months| to| complete| their| journey|.| However|,| they| don|'|t| travel| the| entire| distance| at| once|.| Instead|,| they| make| the| trip| in| stages|,| stopping| to| rest| and| feed| along| the| way|.| \n",
      "\n",
      "The| entire| round|-|t|rip| migration| can| be| up| to| 3|,|0|0|0| miles| long|,| which| is| quite| an| incredible| feat| for| such| a| small| creature|!| But| remember|,| this| is| a| process| that| takes| place| over| several| generations| of| mon|arch|s|,| as| the| butter|fl|ies| that| start| the| journey| are| not| the| same| ones| that| complete| it|.||"
     ]
    }
   ],
   "source": [
    "async for chunk in llm.astream(\n",
    "    \"How long does it take for monarch butterflies to migrate?\"\n",
    "):\n",
    "    print(chunk.content, end=\"|\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6RrXHC_XqWc1",
   "metadata": {
    "id": "6RrXHC_XqWc1"
   },
   "source": [
    "## Supported models\n",
    "\n",
    "Querying `available_models` will still give you all of the other models offered by your API credentials.\n",
    "\n",
    "The `playground_` prefix is optional."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "5b8a312d-38e9-4528-843e-59451bdadbac",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "['playground_nvolveqa_40k',\n",
       " 'playground_llama2_70b',\n",
       " 'playground_mistral_7b',\n",
       " 'playground_sdxl',\n",
       " 'playground_nemotron_steerlm_8b',\n",
       " 'playground_nv_llama2_rlhf_70b',\n",
       " 'playground_neva_22b',\n",
       " 'playground_steerlm_llama_70b',\n",
       " 'playground_llama2_13b',\n",
       " 'playground_llama2_code_13b',\n",
       " 'playground_fuyu_8b',\n",
       " 'playground_nemotron_qa_8b',\n",
       " 'playground_llama2_code_34b',\n",
       " 'playground_mixtral_8x7b',\n",
       " 'playground_clip',\n",
       " 'playground_yi_34b']"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "list(llm.available_models)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d8a407c6-e38b-4cfc-9a33-bcafadc18cf2",
   "metadata": {},
   "source": [
    "## Model types"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "WMW79Iegqj4e",
   "metadata": {
    "id": "WMW79Iegqj4e"
   },
   "source": [
    "All of these models above are supported and can be accessed via `ChatNVIDIA`. \n",
    "\n",
    "Some model types support unique prompting techniques and chat messages. We will review a few important ones below.\n",
    "\n",
    "**To find out more about a specific model, please navigate to the API section of an AI Foundation model [as linked here](https://catalog.ngc.nvidia.com/orgs/nvidia/teams/ai-foundation/models/codellama-13b/api).**"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "03d65053-59fe-40cf-a2d0-55d3dbb13585",
   "metadata": {},
   "source": [
    "### General Chat\n",
    "\n",
    "Models such as `llama2_13b` and `mixtral_8x7b` are good all-around models that you can use for with any LangChain chat messages. Example below."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "f5f7aee8-e90c-4d5a-ac97-0dd3d45c3f4c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hey there! My name is Fred! *giggle* I'm here to help you with any questions or tasks you might have. What can I assist you with today? 😊"
     ]
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [(\"system\", \"You are a helpful AI assistant named Fred.\"), (\"user\", \"{input}\")]\n",
    ")\n",
    "chain = prompt | ChatNVIDIA(model=\"llama2_13b\") | StrOutputParser()\n",
    "\n",
    "for txt in chain.stream({\"input\": \"What's your name?\"}):\n",
    "    print(txt, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "04146118-281b-42ef-b781-2fadeeeea6c8",
   "metadata": {},
   "source": [
    "### Code Generation\n",
    "\n",
    "These models accept the same arguments and input structure as regular chat models, but they tend to perform better on code-genreation and structured code tasks. An example of this is `llama2_code_13b`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "49aa569b-5f33-47b3-9edc-df58313eb038",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "def fizz_buzz(n):\n",
      "    if n % 3 == 0 and n % 5 == 0:\n",
      "        return \"FizzBuzz\"\n",
      "    elif n % 3 == 0:\n",
      "        return \"Fizz\"\n",
      "    elif n % 5 == 0:\n",
      "        return \"Buzz\"\n",
      "    else:\n",
      "        return str(n)\n",
      "\n",
      "fizz_buzz(15)"
     ]
    }
   ],
   "source": [
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are an expert coding AI. Respond only in valid python; no narration whatsoever.\",\n",
    "        ),\n",
    "        (\"user\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "chain = prompt | ChatNVIDIA(model=\"llama2_code_13b\") | StrOutputParser()\n",
    "\n",
    "for txt in chain.stream({\"input\": \"How do I solve this fizz buzz problem?\"}):\n",
    "    print(txt, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "642a618a-faa3-443e-99c3-67b8142f3c51",
   "metadata": {},
   "source": [
    "## Steering LLMs\n",
    "\n",
    "> [SteerLM-optimized models](https://developer.nvidia.com/blog/announcing-steerlm-a-simple-and-practical-technique-to-customize-llms-during-inference/) supports \"dynamic steering\" of model outputs at inference time.\n",
    "\n",
    "This lets you \"control\" the complexity, verbosity, and creativity of the model via integer labels on a scale from 0 to 9. Under the hood, these are passed as a special type of assistant message to the model.\n",
    "\n",
    "The \"steer\" models support this type of input, such as `nemotron_steerlm_8b`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "36a96b1a-e3e7-4ae3-b4b0-9331b5eca04f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Un-creative\n",
      "\n",
      "A peanut butter and jelly sandwich.\n",
      "\n",
      "\n",
      "Creative\n",
      "\n",
      "A PB&J is a sandwich commonly eaten in the United States. It consists of a slice of bread with peanut butter and jelly on it. The sandwich is often eaten for lunch or as a snack.\n",
      "\n",
      "The origins of the PB&J sandwich are not clear, but it is believed to have been invented in the 1920s or 1930s. It became popular during the Great Depression, when peanut butter and jelly were affordable and easy to obtain.\n",
      "\n",
      "Today, the PB&J sandwich is a classic American sandwich that is enjoyed by people of all ages. It is often served in schools and workplaces, and is a popular choice for takeout and delivery.\n",
      "\n",
      "While there are many variations of the PB&J sandwich, the classic version consists of two slices of bread with peanut butter and jelly spread on one or both slices. The sandwich can be topped with additional ingredients, such as nuts, chocolate chips, or banana slices, but the basic combination of peanut butter and jelly remains the same.\n"
     ]
    }
   ],
   "source": [
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "llm = ChatNVIDIA(model=\"nemotron_steerlm_8b\")\n",
    "# Try making it uncreative and not verbose\n",
    "complex_result = llm.invoke(\n",
    "    \"What's a PB&J?\", labels={\"creativity\": 0, \"complexity\": 3, \"verbosity\": 0}\n",
    ")\n",
    "print(\"Un-creative\\n\")\n",
    "print(complex_result.content)\n",
    "\n",
    "# Try making it very creative and verbose\n",
    "print(\"\\n\\nCreative\\n\")\n",
    "creative_result = llm.invoke(\n",
    "    \"What's a PB&J?\", labels={\"creativity\": 9, \"complexity\": 3, \"verbosity\": 9}\n",
    ")\n",
    "print(creative_result.content)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "75849e7a-2adf-4038-8d9d-8a9e12417789",
   "metadata": {},
   "source": [
    "#### Use within LCEL\n",
    "\n",
    "The labels are passed as invocation params. You can `bind` these to the LLM using the `bind` method on the LLM to include it within a declarative, functional chain. Below is an example."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "ae1105c3-2a0c-4db3-916e-24d5e427bd01",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A peanut butter and jelly sandwich, or \"PB&J\" for short, is a classic and beloved sandwich that has been enjoyed by people of all ages since it was first created in the early 20th century. Here are some reasons why it's considered a classic:\n",
      "\n",
      "1. Simple and Versatile: The combination of peanut butter and jelly is simple and versatile, making it a great option for a quick and easy snack or lunch.\n",
      "2. Classic Flavors: The flavors of peanut butter and jelly are classic and timeless, making it a popular choice for people of all ages.\n",
      "3. Easy to Make: A PB&J is one of the easiest sandwiches to make, requiring only a few simple ingredients and a few minutes to assemble.\n",
      "4. Affordable: Unlike many other sandwiches, a PB&J is relatively inexpensive to make, making it a great option for budget-conscious individuals.\n",
      "5. Portable: A PB&J is a portable sandwich, making it a great option for on-the-go eating.\n",
      "6. Nostalgic: The PB&J has become a nostalgic food, associated with childhood and memories of eating it as a kid.\n",
      "\n",
      "Overall, the simplicity, classic flavors, affordability, portability, and nostalgic associations of the PB&J make it a beloved and enduring sandwich that will likely continue to be enjoyed for generations to come."
     ]
    }
   ],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [(\"system\", \"You are a helpful AI assistant named Fred.\"), (\"user\", \"{input}\")]\n",
    ")\n",
    "chain = (\n",
    "    prompt\n",
    "    | ChatNVIDIA(model=\"nemotron_steerlm_8b\").bind(\n",
    "        labels={\"creativity\": 9, \"complexity\": 0, \"verbosity\": 9}\n",
    "    )\n",
    "    | StrOutputParser()\n",
    ")\n",
    "\n",
    "for txt in chain.stream({\"input\": \"Why is a PB&J?\"}):\n",
    "    print(txt, end=\"\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f465ff6-5922-41d8-8abb-1d1e4095cc27",
   "metadata": {},
   "source": [
    "## Multimodal\n",
    "\n",
    "NVIDIA also supports multimodal inputs, meaning you can provide both images and text for the model to reason over. An example model supporting multimodal inputs is `playground_neva_22b`.\n",
    "\n",
    "\n",
    "These models accept LangChain's standard image formats, and accept `labels`, similar to the Steering LLMs above. In addition to `creativity`, `complexity`, and `verbosity`, these models support a `quality` toggle.\n",
    "\n",
    "Below is an example use:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "26625437-1695-440f-b792-b85e6add9a90",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/jpeg": "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",
      "text/plain": [
       "<IPython.core.display.Image object>"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import requests\n",
    "from IPython.display import Image\n",
    "\n",
    "image_url = \"https://picsum.photos/seed/kitten/300/200\"\n",
    "image_content = requests.get(image_url).content\n",
    "\n",
    "Image(image_content)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "dfbbe57c-27a5-4cbb-b967-19c4e7d29fd0",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "llm = ChatNVIDIA(model=\"playground_neva_22b\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ddcb8f1-9cd8-4376-963d-af61c29b2a3c",
   "metadata": {},
   "source": [
    "#### Passing an image as a URL"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "432ea2a2-4d39-43f8-a236-041294171f14",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='The image depicts a scenic forest road surrounded by tall trees and lush greenery. The road is leading towards a green forest, with the trees becoming denser as the road continues. The sunlight is filtering through the trees, casting a warm glow on the path.\\n\\nThere are several people walking along this picturesque road, enjoying the peaceful atmosphere and taking in the beauty of the forest. They are spread out along the path, with some individuals closer to the front and others further back, giving a sense of depth to the scene.')"
      ]
     },
     "execution_count": 14,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "llm.invoke(\n",
    "    [\n",
    "        HumanMessage(\n",
    "            content=[\n",
    "                {\"type\": \"text\", \"text\": \"Describe this image:\"},\n",
    "                {\"type\": \"image_url\", \"image_url\": {\"url\": image_url}},\n",
    "            ]\n",
    "        )\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "af06e3e1-2a67-4b14-814d-b7b7bc035975",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='The image depicts a scenic forest road surrounded by trees and grass.')"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### You can specify the labels for steering here as well.  You can try setting a low verbosity, for instance\n",
    "\n",
    "from langchain_core.messages import HumanMessage\n",
    "\n",
    "llm.invoke(\n",
    "    [\n",
    "        HumanMessage(\n",
    "            content=[\n",
    "                {\"type\": \"text\", \"text\": \"Describe this image:\"},\n",
    "                {\"type\": \"image_url\", \"image_url\": {\"url\": image_url}},\n",
    "            ]\n",
    "        )\n",
    "    ],\n",
    "    labels={\"creativity\": 0, \"quality\": 9, \"complexity\": 0, \"verbosity\": 0},\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0573dd1f-9a17-4c99-ab2a-8d930b89d283",
   "metadata": {},
   "source": [
    "#### Passing an image as a base64 encoded string"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "8c721629-42eb-4006-bf68-0296f7925ebc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='The image depicts a scenic forest road surrounded by tall trees and lush greenery. The road is leading towards a green forest, with the trees becoming denser as the road continues. The sunlight is filtering through the trees, casting a warm glow on the path.\\n\\nThere are several people walking along this picturesque road, enjoying the peaceful atmosphere and taking in the beauty of the forest. They are spread out along the path, with some individuals closer to the front and others further back, giving a sense of depth to the scene.')"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import base64\n",
    "\n",
    "b64_string = base64.b64encode(image_content).decode(\"utf-8\")\n",
    "llm.invoke(\n",
    "    [\n",
    "        HumanMessage(\n",
    "            content=[\n",
    "                {\"type\": \"text\", \"text\": \"Describe this image:\"},\n",
    "                {\n",
    "                    \"type\": \"image_url\",\n",
    "                    \"image_url\": {\"url\": f\"data:image/png;base64,{b64_string}\"},\n",
    "                },\n",
    "            ]\n",
    "        )\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ba958424-28d7-4bc2-9c8e-bd571066853f",
   "metadata": {},
   "source": [
    "#### Directly within the string\n",
    "\n",
    "The NVIDIA API uniquely accepts images as base64 images inlined within `<img/>` HTML tags. While this isn't interoperable with other LLMs, you can directly prompt the model accordingly."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "00c06a9a-497b-4192-a842-b075e27401aa",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='The image depicts a scenic forest road surrounded by tall trees and lush greenery. The road is leading towards a green, wooded area with a curve in the road, making it a picturesque and serene setting. Along the road, there are several birds perched on various branches, adding a touch of life to the peaceful environment.\\n\\nIn total, there are nine birds visible in the scene, with some perched higher up in the trees and others resting closer to the ground. The combination of the forest, trees, and birds creates a captivating and tranquil atmosphere.')"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "base64_with_mime_type = f\"data:image/png;base64,{b64_string}\"\n",
    "llm.invoke(f'What\\'s in this image?\\n<img src=\"{base64_with_mime_type}\" />')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1cd6249a-7ffa-4886-b7e8-5778dc93499e",
   "metadata": {},
   "source": [
    "## RAG: Context models\n",
    "\n",
    "NVIDIA also has Q&A models that support a special \"context\" chat message containing retrieved context (such as documents within a RAG chain). This is useful to avoid prompt-injecting the model. The `_qa_` models like `nemotron_qa_8b` support this.\n",
    "\n",
    "**Note:** Only \"user\" (human) and \"context\" chat messages are supported for these models; System or AI messages that would useful in conversational flows are not supported."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "f994b4d3-c1b0-4e87-aad0-a7b487e2aa43",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'the peace accord'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from langchain_core.messages import ChatMessage\n",
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_nvidia_ai_endpoints import ChatNVIDIA\n",
    "\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        ChatMessage(\n",
    "            role=\"context\", content=\"Parrots and Cats have signed the peace accord.\"\n",
    "        ),\n",
    "        (\"user\", \"{input}\"),\n",
    "    ]\n",
    ")\n",
    "llm = ChatNVIDIA(model=\"nemotron_qa_8b\")\n",
    "chain = prompt | llm | StrOutputParser()\n",
    "chain.invoke({\"input\": \"What was signed?\"})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "137662a6",
   "metadata": {
    "id": "137662a6"
   },
   "source": [
    "## Example usage within a Conversation Chains"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "79efa62d",
   "metadata": {
    "id": "79efa62d"
   },
   "source": [
    "Like any other integration, ChatNVIDIA is fine to support chat utilities like conversation buffers by default. Below, we show the [LangChain ConversationBufferMemory](https://python.langchain.com/docs/modules/memory/types/buffer) example applied to the `mixtral_8x7b` model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "082ccb21-91e1-4e71-a9ba-4bff1e89f105",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install -U --quiet langchain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "fd2c6bc1",
   "metadata": {
    "id": "fd2c6bc1"
   },
   "outputs": [],
   "source": [
    "from langchain.chains import ConversationChain\n",
    "from langchain.memory import ConversationBufferMemory\n",
    "\n",
    "chat = ChatNVIDIA(model=\"mixtral_8x7b\", temperature=0.1, max_tokens=100, top_p=1.0)\n",
    "\n",
    "conversation = ConversationChain(llm=chat, memory=ConversationBufferMemory())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "f644ff28",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 268
    },
    "id": "f644ff28",
    "outputId": "bae354cc-2118-4e01-ce20-a717ac94d27d"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"Hello! I'm here to help answer your questions and engage in a friendly conversation. How can I assist you today? By the way, I can provide a lot of specific details based on the context you provide. If I don't know the answer to something, I'll let you know honestly.\\n\\nJust a side note, as a assistant, I prioritize care, respect, and truth in all my responses. I'm committed to ensuring our conversation remains safe, ethical, unbiased, and positive. I'm looking forward to our discussion!\""
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conversation.invoke(\"Hi there!\")[\"response\"]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "uHIMZxVSVNBC",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 284
    },
    "id": "uHIMZxVSVNBC",
    "outputId": "79acc89d-a820-4f2c-bac2-afe99da95580"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"That's great! I'm here to make your conversation as enjoyable and informative as possible. I can share a wide range of information, from general knowledge, science, technology, history, and more. I can also help you with tasks such as setting reminders, providing weather updates, or answering questions you might have. What would you like to talk about or know?\\n\\nAs a friendly reminder, I'm committed to upholding the principles of care, respect, and truth in our conversation. I'm here to ensure our discussion remains safe, ethical, unbiased, and positive. I'm looking forward to learning more about your interests!\""
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conversation.invoke(\"I'm doing well! Just having a conversation with an AI.\")[\n",
    "    \"response\"\n",
    "]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "LyD1xVKmVSs4",
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 350
    },
    "id": "LyD1xVKmVSs4",
    "outputId": "a1714513-a8fd-4d14-f974-233e39d5c4f5"
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "\"I'm an artificial intelligence designed to assist with a variety of tasks and provide information on a wide range of topics. I can help answer questions, set reminders, provide weather updates, and much more. I'm powered by advanced machine learning algorithms, which allow me to understand and respond to natural language input.\\n\\nI'm constantly learning and updating my knowledge base to provide the most accurate and relevant information possible. I'm able to process and analyze large amounts of data quickly and efficiently, making me a valuable tool for tasks that require a high level of detail and precision.\\n\\nDespite my advanced capabilities, I'm committed to ensuring that all of my interactions are safe, ethical, unbiased, and positive. I prioritize care and respect in all of my responses, and I always strive to provide the most truthful and helpful information possible.\\n\\nI'm excited to be here and to have the opportunity to assist you. Is there anything specific you would like to know or talk about? I'm here to help!\""
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "conversation.invoke(\"Tell me about yourself.\")[\"response\"]"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
